package com.siki.provider.service.Impl;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.siki.provider.dto.invoice.SaveInvoiceDTO;
import com.siki.provider.service.InvoiceService;
import com.siki.provider.vo.invoice.InvoiceVO;
import com.siki.salessystemcommon.entity.Commodity;
import com.siki.salessystemcommon.entity.Invoice;
import com.siki.salessystemcommon.entity.Purchase;
import com.siki.salessystemcommon.entity.enumeration.InvoiceStatusEnum;
import com.siki.salessystemcommon.entity.enumeration.PurchaseStatusEnum;
import com.siki.salessystemcommon.mapper.CommodityMapper;
import com.siki.salessystemcommon.mapper.InvoiceMapper;
import com.siki.salessystemcommon.mapper.PurchaseMapper;
import com.siki.salessystemcommon.utils.DecimalCalculation;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @Author Siki
 * @Date 2020/12/15
 */
@Service
@Transactional(rollbackFor = Exception.class)
public class InvoiceServiceImpl implements InvoiceService {
    private final InvoiceMapper invoiceMapper;
    private final CommodityMapper commodityMapper;
    private final PurchaseMapper purchaseMapper;

    public InvoiceServiceImpl(InvoiceMapper invoiceMapper,
                              CommodityMapper commodityMapper, PurchaseMapper purchaseMapper) {
        this.invoiceMapper = invoiceMapper;
        this.commodityMapper = commodityMapper;
        this.purchaseMapper = purchaseMapper;
    }

    @Override
    public synchronized void saveInvoice(SaveInvoiceDTO dto) {
        Commodity commodity = getCommodityById(dto.getCommodityId());
        Assert.isTrue(dto.getCommodityAmount() <= commodity.getCommodityInventory(),
                "库存不足,请等待补货");
        // 更改清单与商品的出货状态
        purchaseMapper.updatePurchaseAndCommodity(dto.getPurchaseId(), Map.of(
                "shippedStatus", 1,
                "commodityId", dto.getCommodityId()
        ));
        // 库存更改
        commodity.setCommodityInventory(commodity
                .getCommodityInventory() - dto.getCommodityAmount());
        commodityMapper.updateById(commodity);
        // 出货单信息
        Invoice invoice = new Invoice();
        invoice.setInvoiceStatus(InvoiceStatusEnum.NO_PAID)
                .setCommodityAmount(dto.getCommodityAmount())
                .setCommodityId(commodity.getId())
                .setPurchaseId(getPurchaseById(dto.getPurchaseId()).getId());
        invoiceMapper.insert(invoice);
    }

    @Override
    public void deleteInvoice(Long id) {
        var invoice = getInvoiceById(id);
        Assert.isTrue(invoice.getInvoiceStatus()
                        .equals(InvoiceStatusEnum.NO_PAID),
                "只有未付款的出货单可以删除");
        // 更改清单与商品的出货状态
        purchaseMapper.updatePurchaseAndCommodity(invoice.getPurchaseId(), Map.of(
                "shippedStatus", 0,
                "commodityId", invoice.getCommodityId()
        ));
        // 清单中的商品回滚到库存中
        var commodity = getCommodityById(invoice.getCommodityId());
        commodity.setCommodityInventory(invoice.getCommodityAmount() +
                commodity.getCommodityInventory());
        commodityMapper.updateById(commodity);
        // 删除清单
        invoiceMapper.deleteById(id);
    }

    @Override
    public void paidInvoice(Long id) {
        // 更新出货单状态
        var invoice = getInvoiceById(id);
        invoice.setInvoiceStatus(InvoiceStatusEnum.NOT_SHIPPED);
        invoiceMapper.updateById(invoice);
        // 将已支付金额保存至清单
        var purchase = getPurchaseById(invoice.getPurchaseId());
        // 清单中原有的已付款金额+(进货单中商品价格*商品数量)
        String purchaseTotalPaidMoney = DecimalCalculation
                .add(purchase.getPurchasePaidMoney(),
                        DecimalCalculation
                                .mul(String.valueOf(invoice.getCommodityAmount()),
                                        getCommodityById(invoice.getCommodityId()).getCommodityPrice()));
        purchase.setPurchasePaidMoney(purchaseTotalPaidMoney);
        if (purchase.getPurchaseTotalMoney().equals(purchaseTotalPaidMoney)) {
            purchase.setPurchaseStatus(PurchaseStatusEnum.PAID);
        }
        purchaseMapper.updateById(purchase);
    }

    @Override
    public IPage<InvoiceVO> pageInvoice(Long purchaseId, Long pageNo, Long pageSize) {
        return new Page<InvoiceVO>(pageNo, pageSize)
                .setRecords(invoiceMapper.selectPage(new Page<>(pageNo,
                                pageSize),
                        new QueryWrapper<Invoice>()
                                .eq("purchase_id", purchaseId))
                        .getRecords()
                        .stream().map(Invoice::getId)
                        .map(this::getInvoice)
                        .collect(Collectors.toList()));
    }

    @Override
    public InvoiceVO getInvoice(Long id) {
        Invoice invoice = getInvoiceById(id);
        return new InvoiceVO(Map.of(
                "commodityName", getCommodityById(invoice.getCommodityId())
                        .getCommodityName(),
                "commodityAmount", String
                        .valueOf(invoice.getCommodityAmount()),
                "invoiceStatus", Optional.ofNullable(invoice.getInvoiceStatus())
                        .map(String::valueOf).orElse("null")
        ));
    }


    /**
     * [私有方法] - 根据id获取商品
     *
     * @param id 商品id
     * @return Commodity商品
     */
    private Commodity getCommodityById(Long id) {
        return Optional.ofNullable(commodityMapper.selectById(id))
                .orElseThrow(() -> new RuntimeException("该id:" + id + "有误"));
    }

    /**
     * [私有方法] - 根据id查询采购清单
     *
     * @param id 采购清单id
     * @return Purchase采购清单
     */
    private Purchase getPurchaseById(Long id) {
        return Optional.ofNullable(purchaseMapper.selectById(id))
                .orElseThrow(() -> new RuntimeException("该id:" + id + "有误"));
    }

    /**
     * [私有方法] - 根据id获取出货单
     *
     * @param id 商品id
     * @return invoice出货单
     */
    private Invoice getInvoiceById(Long id) {
        return Optional.ofNullable(invoiceMapper.selectById(id))
                .orElseThrow(() -> new RuntimeException("该id:" + id + "有误"));
    }
}
